// Copyright Epic Games, Inc. All Rights Reserved.

#include "LyraRangedWeaponInstance.h"
#include "NativeGameplayTags.h"
#include "GameFramework/Pawn.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Camera/LyraCameraComponent.h"
#include "Physics/PhysicalMaterialWithTags.h"
#include "Weapons/LyraWeaponInstance.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraRangedWeaponInstance)

// 定义游戏标签：稳定瞄准摄像机
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_Lyra_Weapon_SteadyAimingCamera, "Lyra.Weapon.SteadyAimingCamera");

/**
 * ULyraRangedWeaponInstance 构造函数。
 * @param ObjectInitializer 对象初始化器
 */
ULyraRangedWeaponInstance::ULyraRangedWeaponInstance(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// 初始化热量到每发热量曲线：默认在0.0f处添加键值1.0f
	HeatToHeatPerShotCurve.EditorCurveData.AddKey(0.0f, 1.0f);
	// 初始化热量到每秒冷却曲线：默认在0.0f处添加键值2.0f
	HeatToCoolDownPerSecondCurve.EditorCurveData.AddKey(0.0f, 2.0f);
}

/**
 * 资源加载后调用。
 */
void ULyraRangedWeaponInstance::PostLoad()
{
	Super::PostLoad();

// 编辑器模式下更新调试可视化
#if WITH_EDITOR
	UpdateDebugVisualization();
#endif
}

// 编辑器相关代码
#if WITH_EDITOR
/**
 * 属性更改后处理。
 * @param PropertyChangedEvent 属性更改事件
 */
void ULyraRangedWeaponInstance::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);
	// 更新调试可视化
	UpdateDebugVisualization();
}

/**
 * 更新调试可视化数据。
 */
void ULyraRangedWeaponInstance::UpdateDebugVisualization()
{
	// 计算热量范围
	ComputeHeatRange(/*out*/ Debug_MinHeat, /*out*/ Debug_MaxHeat);
	// 计算扩散范围
	ComputeSpreadRange(/*out*/ Debug_MinSpreadAngle, /*out*/ Debug_MaxSpreadAngle);
	// 更新当前热量
	Debug_CurrentHeat = CurrentHeat;
	// 更新当前扩散角度
	Debug_CurrentSpreadAngle = CurrentSpreadAngle;
	// 更新当前扩散角度乘数
	Debug_CurrentSpreadAngleMultiplier = CurrentSpreadAngleMultiplier;
}
#endif

/**
 * 装备武器时调用。
 */
void ULyraRangedWeaponInstance::OnEquipped()
{
	Super::OnEquipped();

	// 从中间开始热量
	float MinHeatRange;
	float MaxHeatRange;
	ComputeHeatRange(/*out*/ MinHeatRange, /*out*/ MaxHeatRange);
	CurrentHeat = (MinHeatRange + MaxHeatRange) * 0.5f;

	// 推导扩散
	CurrentSpreadAngle = HeatToSpreadCurve.GetRichCurveConst()->Eval(CurrentHeat);

	// 默认乘数为1倍
	CurrentSpreadAngleMultiplier = 1.0f;
	StandingStillMultiplier = 1.0f;
	JumpFallMultiplier = 1.0f;
	CrouchingMultiplier = 1.0f;
}

/**
 * 卸下武器时调用。
 */
void ULyraRangedWeaponInstance::OnUnequipped()
{
	Super::OnUnequipped();
}

/**
 * 每帧更新武器状态。
 * @param DeltaSeconds 帧间隔时间
 */
void ULyraRangedWeaponInstance::Tick(float DeltaSeconds)
{
	APawn* Pawn = GetPawn();
	check(Pawn != nullptr);
	
	// 更新扩散和乘数
	const bool bMinSpread = UpdateSpread(DeltaSeconds);
	const bool bMinMultipliers = UpdateMultipliers(DeltaSeconds);

	// 确定是否具有首发精度
	bHasFirstShotAccuracy = bAllowFirstShotAccuracy && bMinMultipliers && bMinSpread;

// 编辑器模式下更新调试可视化
#if WITH_EDITOR
	UpdateDebugVisualization();
#endif
}

/**
 * 计算热量范围。
 * @param MinHeat 输出最小热量
 * @param MaxHeat 输出最大热量
 */
void ULyraRangedWeaponInstance::ComputeHeatRange(float& MinHeat, float& MaxHeat)
{
	float Min1;
	float Max1;
	// 获取热量到每发热量曲线的时间范围
	HeatToHeatPerShotCurve.GetRichCurveConst()->GetTimeRange(/*out*/ Min1, /*out*/ Max1);

	float Min2;
	float Max2;
	// 获取热量到每秒冷却曲线的时间范围
	HeatToCoolDownPerSecondCurve.GetRichCurveConst()->GetTimeRange(/*out*/ Min2, /*out*/ Max2);

	float Min3;
	float Max3;
	// 获取热量到扩散曲线的时间范围
	HeatToSpreadCurve.GetRichCurveConst()->GetTimeRange(/*out*/ Min3, /*out*/ Max3);

	// 计算最小和最大热量范围
	MinHeat = FMath::Min(FMath::Min(Min1, Min2), Min3);
	MaxHeat = FMath::Max(FMath::Max(Max1, Max2), Max3);
}

/**
 * 计算扩散角度范围。
 * @param MinSpread 输出最小扩散角度
 * @param MaxSpread 输出最大扩散角度
 */
void ULyraRangedWeaponInstance::ComputeSpreadRange(float& MinSpread, float& MaxSpread)
{
	// 获取热量到扩散曲线的值范围
	HeatToSpreadCurve.GetRichCurveConst()->GetValueRange(/*out*/ MinSpread, /*out*/ MaxSpread);
}

/**
 * 增加扩散（开火后调用）。
 */
void ULyraRangedWeaponInstance::AddSpread()
{
	// 采样热量上升曲线
	const float HeatPerShot = HeatToHeatPerShotCurve.GetRichCurveConst()->Eval(CurrentHeat);
	// 增加热量并钳制
	CurrentHeat = ClampHeat(CurrentHeat + HeatPerShot);

	// 将热量映射到扩散角度
	CurrentSpreadAngle = HeatToSpreadCurve.GetRichCurveConst()->Eval(CurrentHeat);

// 编辑器模式下更新调试可视化
#if WITH_EDITOR
	UpdateDebugVisualization();
#endif
}

/**
 * 获取距离衰减乘数。
 * @param Distance 距离
 * @param SourceTags 源标签容器
 * @param TargetTags 目标标签容器
 * @return 返回距离衰减乘数
 */
float ULyraRangedWeaponInstance::GetDistanceAttenuation(float Distance, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags) const
{
	const FRichCurve* Curve = DistanceDamageFalloff.GetRichCurveConst();
	// 如果曲线有数据则评估，否则返回1.0（无衰减）
	return Curve->HasAnyData() ? Curve->Eval(Distance) : 1.0f;
}

/**
 * 获取物理材质衰减乘数。
 * @param PhysicalMaterial 物理材质
 * @param SourceTags 源标签容器
 * @param TargetTags 目标标签容器
 * @return 返回物理材质衰减乘数
 */
float ULyraRangedWeaponInstance::GetPhysicalMaterialAttenuation(const UPhysicalMaterial* PhysicalMaterial, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags) const
{
	float CombinedMultiplier = 1.0f;
	// 检查是否为带标签的物理材质
	if (const UPhysicalMaterialWithTags* PhysMatWithTags = Cast<const UPhysicalMaterialWithTags>(PhysicalMaterial))
	{
		// 遍历物理材质的所有标签
		for (const FGameplayTag MaterialTag : PhysMatWithTags->Tags)
		{
			// 查找材质标签对应的伤害乘数
			if (const float* pTagMultiplier = MaterialDamageMultiplier.Find(MaterialTag))
			{
				// 乘性组合乘数
				CombinedMultiplier *= *pTagMultiplier;
			}
		}
	}

	return CombinedMultiplier;
}

/**
 * 更新扩散并返回扩散是否处于最小值。
 * @param DeltaSeconds 帧间隔时间
 * @return 如果扩散处于最小值返回true
 */
bool ULyraRangedWeaponInstance::UpdateSpread(float DeltaSeconds)
{
	// 计算自上次开火以来的时间
	const float TimeSinceFired = GetWorld()->TimeSince(LastFireTime);

	// 如果超过冷却恢复延迟，开始冷却
	if (TimeSinceFired > SpreadRecoveryCooldownDelay)
	{
		// 根据当前热量获取冷却速率
		const float CooldownRate = HeatToCoolDownPerSecondCurve.GetRichCurveConst()->Eval(CurrentHeat);
		// 减少热量并钳制
		CurrentHeat = ClampHeat(CurrentHeat - (CooldownRate * DeltaSeconds));
		// 更新扩散角度
		CurrentSpreadAngle = HeatToSpreadCurve.GetRichCurveConst()->Eval(CurrentHeat);
	}
	
	float MinSpread;
	float MaxSpread;
	// 计算扩散范围
	ComputeSpreadRange(/*out*/ MinSpread, /*out*/ MaxSpread);

	// 检查当前扩散角度是否接近最小值
	return FMath::IsNearlyEqual(CurrentSpreadAngle, MinSpread, KINDA_SMALL_NUMBER);
}

/**
 * 更新乘数并返回它们是否处于最小值。
 * @param DeltaSeconds 帧间隔时间
 * @return 如果乘数处于最小值返回true
 */
bool ULyraRangedWeaponInstance::UpdateMultipliers(float DeltaSeconds)
{
	const float MultiplierNearlyEqualThreshold = 0.05f; // 乘数近似相等阈值

	APawn* Pawn = GetPawn();
	check(Pawn != nullptr);
	UCharacterMovementComponent* CharMovementComp = Cast<UCharacterMovementComponent>(Pawn->GetMovementComponent());

	// 检查是否静止，如果是，平滑应用奖励
	const float PawnSpeed = Pawn->GetVelocity().Size(); // Pawn速度
	const float MovementTargetValue = FMath::GetMappedRangeValueClamped(
		/*InputRange=*/ FVector2D(StandingStillSpeedThreshold, StandingStillSpeedThreshold + StandingStillToMovingSpeedRange),
		/*OutputRange=*/ FVector2D(SpreadAngleMultiplier_StandingStill, 1.0f),
		/*Alpha=*/ PawnSpeed); // 根据速度映射目标值
	// 插值到目标值
	StandingStillMultiplier = FMath::FInterpTo(StandingStillMultiplier, MovementTargetValue, DeltaSeconds, TransitionRate_StandingStill);
	// 检查静止乘数是否接近最小值
	const bool bStandingStillMultiplierAtMin = FMath::IsNearlyEqual(StandingStillMultiplier, SpreadAngleMultiplier_StandingStill, SpreadAngleMultiplier_StandingStill*0.1f);

	// 检查是否蹲伏，如果是，平滑应用奖励
	const bool bIsCrouching = (CharMovementComp != nullptr) && CharMovementComp->IsCrouching();
	const float CrouchingTargetValue = bIsCrouching ? SpreadAngleMultiplier_Crouching : 1.0f; // 蹲伏目标值
	// 插值到目标值
	CrouchingMultiplier = FMath::FInterpTo(CrouchingMultiplier, CrouchingTargetValue, DeltaSeconds, TransitionRate_Crouching);
	// 检查蹲伏乘数是否接近目标值
	const bool bCrouchingMultiplierAtTarget = FMath::IsNearlyEqual(CrouchingMultiplier, CrouchingTargetValue, MultiplierNearlyEqualThreshold);

	// 检查是否在空中（跳跃/下落），如果是，平滑应用惩罚
	const bool bIsJumpingOrFalling = (CharMovementComp != nullptr) && CharMovementComp->IsFalling();
	const float JumpFallTargetValue = bIsJumpingOrFalling ? SpreadAngleMultiplier_JumpingOrFalling : 1.0f; // 跳跃/下落目标值
	// 插值到目标值
	JumpFallMultiplier = FMath::FInterpTo(JumpFallMultiplier, JumpFallTargetValue, DeltaSeconds, TransitionRate_JumpingOrFalling);
	// 检查跳跃/下落乘数是否为1
	const bool bJumpFallMultiplerIs1 = FMath::IsNearlyEqual(JumpFallMultiplier, 1.0f, MultiplierNearlyEqualThreshold);

	// 确定是否在瞄准，并根据摄像机过渡进度应用奖励
	float AimingAlpha = 0.0f; // 瞄准Alpha值
	if (const ULyraCameraComponent* CameraComponent = ULyraCameraComponent::FindCameraComponent(Pawn))
	{
		float TopCameraWeight; // 顶部摄像机权重
		FGameplayTag TopCameraTag; // 顶部摄像机标签
		CameraComponent->GetBlendInfo(/*out*/ TopCameraWeight, /*out*/ TopCameraTag);

		// 如果顶部摄像机标签是稳定瞄准摄像机，使用其权重作为瞄准Alpha
		AimingAlpha = (TopCameraTag == TAG_Lyra_Weapon_SteadyAimingCamera) ? TopCameraWeight : 0.0f;
	}
	// 根据瞄准Alpha值计算瞄准乘数
	const float AimingMultiplier = FMath::GetMappedRangeValueClamped(
		/*InputRange=*/ FVector2D(0.0f, 1.0f),
		/*OutputRange=*/ FVector2D(1.0f, SpreadAngleMultiplier_Aiming),
		/*Alpha=*/ AimingAlpha);
	// 检查瞄准乘数是否接近目标值
	const bool bAimingMultiplierAtTarget = FMath::IsNearlyEqual(AimingMultiplier, SpreadAngleMultiplier_Aiming, KINDA_SMALL_NUMBER);

	// 组合所有乘数
	const float CombinedMultiplier = AimingMultiplier * StandingStillMultiplier * CrouchingMultiplier * JumpFallMultiplier;
	CurrentSpreadAngleMultiplier = CombinedMultiplier;

	// 需要处理这些扩散乘数是否指示我们未处于最小扩散
	return bStandingStillMultiplierAtMin && bCrouchingMultiplierAtTarget && bJumpFallMultiplerIs1 && bAimingMultiplierAtTarget;
}